package one.stand.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.github.vioao.wechat.api.TokenApi;
import com.github.vioao.wechat.bean.response.token.TokenResponse;
import com.github.vioao.wechat.utils.DateUtils;
import one.stand.entity.ResultTemplateEntity;
import one.stand.enums.KeyWordId;
import one.stand.enums.KeyWordType;
import one.stand.enums.LoginEnum;
import one.stand.enums.WxSubscribeEnums;
import one.stand.model.CompanyAccountModel;
import one.stand.model.WxNoticeModel;
import one.stand.request.CompanyAccountRequest;
import one.stand.service.CompanyAccontService;
import one.stand.service.WxMessageService;
import one.stand.util.DateUtil;
import one.stand.util.HttpUtil;
import org.apache.commons.lang.time.DateFormatUtils;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 * @author: QIK
 * @CreateDate: 2019/4/29 11:32
 */
@Service
@Transactional
public class WxMessageServiceImpl extends PublicBaseService implements WxMessageService {


    @Autowired
    private CompanyAccontService companyAccontService;

    /**
     * 得到用户端小程序token
     *
     * @return token
     */
    @Override
    public String getWxToken(String appId, String appSecret) {
        TokenResponse token= TokenApi.token(appId,appSecret);
        log.info("获取微信token:{}", JSON.toJSONString(token));
        return token.getAccessToken();
    }

    /**
     * 发送微信消息模板
     *
     * @parm touser 接收者（用户）的 openid
     * @parm formId 表单提交场景下，为 submit 事件带上的 formId；支付场景下，为本次支付的 prepay_id
     * @parm code 所需下发的模板消息的code
     * @parm fillData 数据型模板数据
     * @parm parameter 参数
     */

    @Override
    public void sendTemplate(String touser, String formId, Integer code, String[] fillData, String parameter, String accessToken, Integer companyId) {
        sendTemplateWx(touser, formId, code, fillData, parameter, accessToken, companyId);
    }

    @Override
    public void sendTemplate(String touser, String formId, Integer code, String[] fillData, String accessToken, Integer companyId) {
        sendTemplateWx(touser, formId, code, fillData, null, accessToken, companyId);
    }

    /**
     *  发送消息 兼容订阅消息和模板消息
     * @param toUser
     * @param formId
     * @param code
     * @param sonDataJson
     * @param page
     * @param accessToken
     * @param companyId
     * @param isSubscribe
     * @return
     */
    @Override
    @Async("asyncServiceExecutor")
    public String sendNewTemplateWx(String toUser, String formId, Integer code, JSONObject sonDataJson, String page,
                                    String accessToken, Integer companyId, boolean isSubscribe, Integer accountType) {
        log.info("微信推送消息参数：openId={},companyId={},code={},page={},\n\taccess-token = {},\n\tbody = {},isSubscribe={}", toUser, companyId, code, page, accessToken, sonDataJson,isSubscribe);
        if (StringUtils.isEmpty(toUser) || null == code || companyId == null) {
            return null;
        }
        WxNoticeModel model = getWxNotice(code, companyId,accountType);
        log.info("Model : {}", model);
        if (model == null) {
            return null;
        }
        CloseableHttpClient httpClient = HttpClients.createDefault();
        String uri;
        //是否是订阅消息
        if (isSubscribe) {
            //訂閲消息地址
            uri = "https://api.weixin.qq.com/cgi-bin/message/subscribe/send?access_token=" + accessToken;
        } else {
            uri = "https://api.weixin.qq.com/cgi-bin/message/wxopen/template/send?access_token=" + accessToken;
        }
        HttpPost httpPost = new HttpPost(uri);
        // 装配post请求参数
        JSONObject json = new JSONObject();
        json.put("touser", toUser);
        json.put("template_id", model.getValue());
        if (StringUtils.isEmpty(page)) {
            json.put("page", model.getPage());
        } else {
            json.put("page", model.getPage() + page);
        }
        //非订阅模式才有的form_id，订阅消息不需要
        if (!isSubscribe) {
            json.put("form_id", formId);
        }
        json.put("data", sonDataJson);
        String resultStr = "发送失败";
        try {
            log.info("send:{}",json);
            StringEntity myEntity = new StringEntity(json.toJSONString(), ContentType.APPLICATION_JSON);
            // 设置post求情参数
            httpPost.setEntity(myEntity);

            HttpResponse httpResponse = httpClient.execute(httpPost);
            if (httpResponse.getStatusLine().getStatusCode() == 200) {
                // 发送成功
                String resutlEntity = EntityUtils.toString(httpResponse.getEntity());
                log.info("推送结果：{}", resutlEntity);

                ResultTemplateEntity resultTemplateDate = JSONObject.parseObject(resutlEntity, ResultTemplateEntity.class);
                if ("40003".equals(resultTemplateDate.getErrcode())) {
                    resultStr = "touser字段openid为空或者不正确";
                }
                if ("40037".equals(resultTemplateDate.getErrcode())) {
                    resultStr = "template_id不正确";
                }
                if ("43101".equals(resultTemplateDate.getErrcode())) {
                    resultStr = "用户拒绝接受消息，如果用户之前曾经订阅过，则表示用户取消了订阅关系";
                }
                if ("47003".equals(resultTemplateDate.getErrcode())) {
                    resultStr = "模板参数不准确，可能为空或者不满足规则，errmsg会提示具体是哪个字段出错:" + resultTemplateDate.getErrmsg();
                }
                if ("41028".equals(resultTemplateDate.getErrcode())) {
                    resultStr = "form_id不正确，或者过期";
                }
                if ("41029".equals(resultTemplateDate.getErrcode())) {
                    resultStr = "form_id已被使用";
                }
                if ("41030".equals(resultTemplateDate.getErrcode())) {
                    resultStr = "page不正确,需要保证在现网版本小程序中存在，与app.json保持一致";
                }
                if ("45009".equals(resultTemplateDate.getErrcode())) {
                    resultStr = "接口调用超过限额（目前默认每个帐号日调用限额为100万）";
                }
                if ("0".equals(resultTemplateDate.getErrcode())) {
                    resultStr = "成功";
                }
                log.info("本次微信推送模板推送结果：{}", resultStr);
                return resultTemplateDate.getErrcode();
            } else {
                // 发送失败
                return httpResponse.getStatusLine().getStatusCode() + "";
            }
        } catch (Exception e) {
            log.error("微信推送消息[异常]", e);
        } finally {
            try {
                if (httpClient != null) {
                    // 释放资源
                    httpClient.close();
                }
            } catch (IOException e) {
                log.error("释放连接资源[异常]", e);
            }
            userMapper.delFormId(formId);
        }
        return resultStr;
    }

    /**
     * 老版本
     * @param touser
     * @param formId
     * @param code
     * @param fillData
     * @param parameter
     * @param accessToken
     * @param companyId
     * @return
     */
    @Override
    @Async("asyncServiceExecutor")
    public String sendTemplateWx(String touser, String formId, Integer code, String[] fillData, String parameter, String accessToken, Integer companyId) {
        return sendNewTemplateWx(touser, formId, code, getDataOldJson(fillData), parameter, accessToken, companyId, false,1);
    }

    private JSONObject getDataOldJson(String[] fillData) {
        JSONObject dataJson = new JSONObject();
        //订阅的消息格式不一样
        for (int i = 0; i < fillData.length; i++) {
            JSONObject sonDateJson = new JSONObject();
            sonDateJson.put("value", fillData[i]);
            dataJson.put("keyword" + (i + 1), sonDateJson);
        }
        return dataJson;
    }


    /**
     * 构造订阅消息文本
     * @param subscribeEnum
     * @param fillData
     * @return
     */
    private JSONObject getDataSubscribeJson(WxSubscribeEnums subscribeEnum, String[] fillData) {
        JSONObject dataJson = new JSONObject();
        //订阅的消息格式不一样
        if (fillData.length == subscribeEnum.getData().length) {
            KeyWordId[] keyWordIds = subscribeEnum.getData();
            for (int i = 0; i < fillData.length; i++) {
                KeyWordId keyWordId = keyWordIds[i];
                String type = keyWordId.getType().getType();
                JSONObject sonDataJson = new JSONObject();
                sonDataJson.put("value", getString(keyWordId.getType(), fillData[i]));
                //TODO keyWordId.getCode() name 和 thing 转换可以放到这里
                dataJson.put(type + keyWordId.getCode(), sonDataJson);
            }
        } else {
            log.warn("------- Fill data 长度不一直，请检查");
        }
        return dataJson;
    }

    private String getString(KeyWordType keyWordType, String s) {
        switch (keyWordType) {
            case date:
                // data 格式 2019年10月1日 15:01
            case time:
                if (StringUtils.isEmpty(s)) {
                    return getTime();
                } else {
                    return s;
                }
            case thing:
                return getThingName(s);
            case name:
                return getShortName(s);
            case phrase:
                return getPhraseString(s);
            default:
                return s;
        }
    }

    public WxNoticeModel getWxNotice(Integer code, Integer companyId,Integer accountType) {
        if (code == null || companyId == null) {
            return null;
        }
        return wxNoticeMapper.select(new WxNoticeModel() {{
            setCompanyId(companyId);
            setCode(code);
            setMsgType(2);
            setType(accountType == 2 ? "CRM" : "USER");
        }});
    }

    @Override
    public void sendTemplate(String touser, String formId, Integer code, String[] fillData, Integer accountType, Integer companyId) {
        String accessToken = accessToken(accountType, touser);
        log.info("模板推送-accessToken：{}", accessToken);
        if (StringUtils.isEmpty(accessToken)) {
            return;
        }
        sendTemplate(touser, formId, code, fillData, accessToken, companyId);
    }

    /**
     * 發送訂閲消息
     * @param touser
     * @param formId
     * @param subscribeEnum
     * @param fillData
     * @param accountType
     * @param companyId
     */
    @Override
    public void sendNewTemplate(String touser, String formId, WxSubscribeEnums subscribeEnum, String[] fillData, Integer accountType, Integer companyId) {
        String accessToken = accessToken(accountType, touser);
        log.info("订阅推送-accessToken：{}", accessToken);
        if (StringUtils.isEmpty(accessToken)) {
            return;
        }
        sendNewTemplateWx(touser, formId, subscribeEnum.getCode(), getDataSubscribeJson(subscribeEnum, fillData), null, accessToken, companyId, true,accountType);
    }

    @Override
    public String sendTemplateWx(String touser, String formId, Integer code, String[] fillData, String parameter, Integer accountType, Integer companyId) {
        String accessToken = accessToken(accountType, touser);
        log.info("模板推送-accessToken：{}", accessToken);
        if (StringUtils.isEmpty(accessToken)) {
            return null;
        }

        return sendTemplateWx(touser, formId, code, fillData, parameter, accessToken, companyId);
    }

    @Override
    public void sendTemplate(String touser, String formId, Integer code, String[] fillData, String parameter, Integer accountType, Integer companyId) {
        String accessToken = accessToken(accountType, touser);
        log.info("模板推送-accessToken：{}", accessToken);
        if (StringUtils.isEmpty(accessToken)) {
            return;
        }
        sendTemplate(touser, formId, code, fillData, parameter, accessToken, companyId);
    }

    @Override
    public void sendNewTemplate(String touser, String formId, WxSubscribeEnums subscribeEnum, String[] fillData, String parameter, Integer accountType, Integer companyId) {
        String accessToken = accessToken(accountType, touser);
        log.info("模板推送-accessToken：{}", accessToken);
        if (StringUtils.isEmpty(accessToken)) {
            return;
        }
        sendNewTemplateWx(touser, formId, subscribeEnum.getCode(), getDataSubscribeJson(subscribeEnum, fillData), parameter, accessToken, companyId, true,accountType);
    }

    @Override
    public String accessToken(Integer accountType, Long userId) {
        CompanyAccountModel companyAccountModel = companyAccontService.selectByUserIdCopy(new CompanyAccountRequest() {{
            setUserId(userId);
            setAccountType(accountType);
        }});
        if (null == companyAccountModel) {
            log.info("获取微信账号信息失败：{}");
            return null;
        }
        String accessToken = getWxToken(companyAccountModel.getAppId(), companyAccountModel.getAppSecret());
        return accessToken;
    }

    public String accessToken(Integer accountType, String openId) {
        CompanyAccountModel companyAccountModel = companyAccontService.selectByUserIdCopy(new CompanyAccountRequest() {{
            if (accountType.equals(LoginEnum.CRM.getAccountType())) {
                setCrmOpenId(openId);
            }
            if (accountType.equals(LoginEnum.USER.getAccountType())) {
                setUserOpenId(openId);
            }
            setAccountType(accountType);
        }});
        if (null == companyAccountModel) {
            log.info("获取微信账号信息失败：{}");
            return null;
        }
        String accessToken = getWxToken(companyAccountModel.getAppId(), companyAccountModel.getAppSecret());
        return accessToken;
    }


    @Override
    public boolean msgSecCheck(String content, Integer companyId) {
        CompanyAccountModel companyAccountModel = getModel(companyId);

        if(companyAccountModel != null){
            String token = this.getWxToken(companyAccountModel.getAppId(),companyAccountModel.getAppSecret());
            JSONObject param = new JSONObject();
            param.put("content", content);
            String url = String.format("https://api.weixin.qq.com/wxa/msg_sec_check?access_token=%s",token);
            String res = HttpUtil.sendPostJson(url, param);
            log.info("安全检查结果:{}",res);

            return JSONObject.parseObject(res).getString("errcode").equals("0");
        }
        return true;
    }

    @Override
    public boolean imgSecCheck(String img, Integer companyId) {
        CompanyAccountModel companyAccountModel = getModel(companyId);
        if(companyAccountModel != null){
            String token = this.getWxToken(companyAccountModel.getAppId(),companyAccountModel.getAppSecret());
            Map<String, String> param = new HashMap<>(4);
            //todo 图片转 FormData
            param.put("media", "FormData");
            param.put("access_token", token);
            String url = String.format("https://api.weixin.qq.com/wxa/img_sec_check?access_token=%s",token);
            String res = HttpUtil.sendPost(url, param);

            return JSONObject.parseObject(res).getString("errcode").equals("0");
        }
        return true;
    }

    private CompanyAccountModel getModel(Integer companyId){
        return this.companyAccontService.selectByCompanyKeyDefault(new CompanyAccountRequest(){{setCompanyId(companyId);}});
    }

    public void test() {

    }

}
